package de.tabuma.soap.logik;

import org.xml.sax.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.logging.*;

import de.tabuma.soap.annotation.*;
import de.tabuma.soap.entity.*;
import de.tabuma.soap.entity.wsdl.*;
import de.tabuma.soap.entity.xsd.*;

/**
 * Methods for element finding
 * @author $Author: alefen $
 * @version $Revision: 1.1 $
 * @since $Date: 2009/12/30 02:53:55 $
 */
public class ObjectElementFacade {

    private static Logger log = Logger.getLogger(ObjectElementFacade.class.getName());

    /**
     * Finds head object for localname and uri
     * @param localName
     * @param uri
     * @return
     * @throws org.xml.sax.SAXException
     */
    public static Object findHeadElement(String localName,String uri) throws SAXException {
        //Checking whether localName fits to tdefinition object

        SoapClassDefinition soapClassDefinition = TDefinitions.class.getAnnotation(SoapClassDefinition.class);
        if(localName.equals(soapClassDefinition.value()[0].mappingString())) {
            return new TDefinitions();
        }

        //checking whether localName fits to xsschema object
        soapClassDefinition = XsSchema.class.getAnnotation(SoapClassDefinition.class);
        if(localName.equals(soapClassDefinition.value()[0].mappingString())) {
            return new XsSchema();
        }

        //if it does not fit, exception will be thrown
        throw new SAXException("Not supported to get "+localName+" as head object.");
    }

    /**
     * Gets element from soap field definition
     * @param localName
     * @param uri
     * @param qName
     * @param headObject
     * @return
     */
    public static Object getElementFromSoapFieldDefinition(String localName, String uri, String qName, Object headObject) {
        //Getting all datafields from headObject
        for (int i = 0; i < headObject.getClass().getDeclaredFields().length; i++) {
            try {
                Field field = headObject.getClass().getDeclaredFields()[i];
                SoapFieldDefinition soapFieldDefinition = field.getAnnotation(SoapFieldDefinition.class);
                //Only working with field that has annotation of soapFieldDefinition
                if (soapFieldDefinition != null) {
                    //Getting all soap Field Definitions
                    for (int j = 0; j < soapFieldDefinition.value().length; j++) {
                        SoapFieldClass soapFieldClass = soapFieldDefinition.value()[j];
                        log.finest("found " + soapFieldClass + " for " + field.getName());
                        Class classDefinition = soapFieldClass.classDefinition();
                        SoapClassDefinition soapClassDefinition = (SoapClassDefinition) classDefinition.getAnnotation(SoapClassDefinition.class);
                        log.finest("found " + soapClassDefinition + " for " + classDefinition);
                        if (soapClassDefinition != null) {
                            for (int k = 0; k < soapClassDefinition.value().length; k++) {
                                SoapClassMapping soapClassMapping = soapClassDefinition.value()[k];
                                if (soapClassMapping.mappingString().equals(localName.toLowerCase())) {
                                    //Creating object
                                    Object myObject = classDefinition.getConstructors()[0].newInstance();
                                    log.fine(myObject.toString());
                                    Object oldObject = myObject;
                                    log.fine(oldObject.toString());
                                    if (soapFieldDefinition.value().length > 1) {
                                        myObject = new AnyElement(myObject);
                                        String str = "";
                                        for (int t = 0; t < myObject.getClass().getDeclaredFields().length; t++) {
                                            try {
                                                Field field1 = myObject.getClass().getDeclaredFields()[t];
                                                str += field1.getName() + ":" + myObject.getClass().getMethod(ObjectMethods.getGetter(field1.getName())).invoke(myObject).toString();
                                            } catch (Exception e) {
                                            }
                                        }
                                        log.fine(str);
                                        oldObject = ((AnyElement) myObject).toObject();
                                    }
                                    
                                    //Save datas
                                    Long id = PMF.doCreate(myObject);
                                    log.fine("type of field " + field.getName() + " is " + field.getType());
                                    if (field.getType().equals(java.util.List.class)) {
                                        log.fine(field.getType() + " | " + headObject);
                                        List<Long> list = (List<Long>) headObject.getClass().getMethod(ObjectMethods.getGetter(field.getName())).invoke(headObject);
                                        list.add(id);
                                        String setter = ObjectMethods.getSetter(field.getName());
                                        log.fine(setter + " | " + headObject + " | " + list);
                                        headObject.getClass().getMethod(setter, List.class).invoke(headObject, list);
                                        PMF.doUpdate(headObject);
                                    } else {
                                        Long id1 = (Long) headObject.getClass().getMethod(ObjectMethods.getGetter(field.getName())).invoke(headObject);
                                        if (id1 != 0) {
                                            throw new SAXException(field.getName() + " in " + headObject + " is alread set to " + id1);
                                        }
                                        headObject.getClass().getMethod(ObjectMethods.getSetter(field.getName()), Long.class).invoke(headObject, id);
                                        PMF.doUpdate(headObject);
                                    }
                                    if(log.getLevel()==Level.FINE || log.getLevel()==Level.FINER || log.getLevel()==Level.FINEST) {
                                        log.fine(oldObject+":"+ObjectMethods.toString(oldObject));
                                        log.fine(myObject+":"+ObjectMethods.toString(myObject));
                                    }
                                    return oldObject;
                                }
                            }
                        } else {
                            log.fine("no classdefinition found in field " + field.getName() + " for " + localName);
                        }
                    }
                } else {
                    log.finest("no fielddefinition found in field " + field.getName() + " for " + localName);
                }
            } catch (Exception e) {
                log.warning(e.getMessage());
                String warning = "";
                for (int i1 = 0; i1 < e.getStackTrace().length; i1++) {
                    warning += e.getStackTrace()[i1] + "\n";
                }
                log.warning(warning);
            }
        }
        return null;
    }

    /**
     * Returns an element that fits to element from extends field
     * @see SoapExtendsDefinition
     * @param localName
     * @param uri
     * @param qName
     * @param headObject
     * @return
     */
    public static Object getElementFromExtendsField(String localName,String uri,String qName,Object headObject) {
        //checking for extends classes
        for(int i = 0 ; i < headObject.getClass().getDeclaredFields().length ; i++) {
            try {
                Field field = headObject.getClass().getDeclaredFields()[i];
                SoapExtendsDefinition soapExtendsDefinition = field.getAnnotation(SoapExtendsDefinition.class);
                if(soapExtendsDefinition!=null) {
                    Long id = (Long) headObject.getClass().getMethod(ObjectMethods.getGetter(field.getName())).invoke(headObject);
                    if(id==0) {
                        Object obj1 = soapExtendsDefinition.extendsClass().getConstructors()[0].newInstance();
                        id = PMF.doCreate(obj1);
                        headObject.getClass().getMethod(ObjectMethods.getSetter(field.getName()),Long.class).invoke(headObject, id);
                        PMF.doUpdate(headObject);
                        Object obj2 = getObject(localName, uri, qName, obj1);
                        if(obj2!=null) {
                            return obj2;
                        }
                    }
                    else {
                        Object obj1 = PMF.get(id, soapExtendsDefinition.extendsClass().getConstructors()[0].newInstance());
                        Object obj2 = getObject(localName, uri, qName, obj1);
                        if(obj2!=null) {
                            return obj2;
                        }
                    }
                }
            }
            catch(Exception e) {
                log.warning(e.getMessage());
                String warning = "";
                for(int i1=0;i1<e.getStackTrace().length;i1++) {
                    warning += e.getStackTrace()[i1]+"\n";
                }
                log.warning(warning);
            }
        }
        return null;
    }

    /**
     * Gets an element that fits to localName and headobject
     * @param localName
     * @param uri
     * @param qName
     * @param headObject
     * @return
     * @throws org.xml.sax.SAXException
     */
    public static Object getObject(String localName,String uri, String qName, Object headObject) throws SAXException {
        if(headObject==null) {
            return findHeadElement(localName,uri);
        }

        log.fine("localName : " + localName + "; uri : " + uri + "; qName : " + qName + "; headObject : " +headObject);

        Object obj = getElementFromSoapFieldDefinition(localName, uri, qName, headObject);
        if(obj!=null) {
            return obj;
        }

        obj = getElementFromExtendsField(localName,uri, qName, headObject);
        if(obj!=null) {
            return obj;
        }

        //if no class was found, an exception will be thrown
        throw new SAXException("Not supported to add "+localName+" to "+headObject);
    }

}